今天大概會聊到的範圍
- ComposeView
 
之前有介紹過 Compose 中的 AndroidView ,是一個可以在 Compose 中放入一般 Android View 。那如果我們要在一般的 Android 專案中放入 Compose 呢? 有幾個方法可以在原先 Android 的專案中加入 Compose
之前有介紹過 Compose compiler 的運作機制,那些轉換都是透過 Kotlin 在 1.5 新增的 JVM IR compiler backend 。另外,Android Studio 也只有在 Arctic Fox 之後的版本才有支援 Compose、Android Build Tool 也需要在 7.0 以上:
// project-level build.gradle
buildscript {
    ...
    dependencies {
        classpath "com.android.tools.build:gradle:7.0.0"
        ...
    }
}
// module-level build.gradle 
android {
    ... 
    kotlinOptions {
        jvmTarget = '1.8'
        useIR = true
    }
    buildFeatures {
        compose true
    }
    composeOptions {
        kotlinCompilerExtensionVersion compose_version
        kotlinCompilerVersion '1.5.10'
    }
    
    ...
}
準備就緒,開始導入 Compose 吧!先加入 Compose 相關的 library:
dependencies {
    // Integration with activities
    implementation 'androidx.activity:activity-compose:1.3.1'
    // Compose Material Design
    implementation 'androidx.compose.material:material:1.0.1'
    // Animations
    implementation 'androidx.compose.animation:animation:1.0.1'
    // Tooling support (Previews, etc.)
    implementation 'androidx.compose.ui:ui-tooling:1.0.1'
    // Integration with ViewModels
    implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:1.0.0-alpha07'
    // UI Tests
    androidTestImplementation 'androidx.compose.ui:ui-test-junit4:1.0.1'
}
如果整個頁面都可以使用 Compose 來建制的話,我們可以用我們學習 Compose 以來常常見到的好朋友 setContent function 來加入 Composable:
// 記得要加入 'androidx.activity:activity-compose:1.3.1'
class InteropActivity : AppCompatActivity() { // <-- 一般的 AppCompactActivity 就有繼承 ComponentActivity
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {    // <-- setContent 是 compose 在 ComponentActivity 身上加上的 extension fucntion    
            ComposeScreen()    // <--- 這裡就可以放 Composable 了
        }
    }
}
如果只有 Activity 中的某個部分想轉成 Compose,在 xml 中使用 ComposeView :
// in xml
<LinearLayout ...>
    ...
    <androidx.compose.ui.platform.ComposeView
        android:id="@+id/interopComposeView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
    ...
</LinearLayout>
// in activity 
class InteropActivity : AppCompatActivity() {
    
    private lateinit var binding: ActivityInteropBinding
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityInteropBinding.inflate(layoutInflater)        // <-- 這裡是用 ViewBinding。一般的 findViewById 也可以喔
        val root = binding.root
        binding.interopComposeView.setContent {
            // composable        // ComposeView 有 setContent  function 可以放入 composable
        }
        
        
        setContentView(root)
    }
}
沒特別設定的話,ComposeView 會在 該 View detach window 時觸發 dispose。但是這可能不是期望的行為,例如在 Fragment 中我們可能希望跟著 Fragment 的 Lifecycle。
ComposeView 有 setViewCompositionStrategy function 可以使用,調整 dispose 的策略:
就這樣,把 ComposeView 當成一般的 View 放在 Android 內,再配合之前研究到的 AndroidView 就可以慢慢將計有專案 migrate 到 Compose 上囉!
Reference: